--[[
Name: oUF_cRefactored
Author: Sonora (The Dragonflight, US Kirin Tor)
Website & SVN: http://code.google.com/p/browniehelper/source/browse/#svn/trunk/oUF_cRefactored
License: GNU General Public License v3

A significant portion of the code appearing in the module was adapted from the
oUF_Caellian project (by Caellian):

http://www.wowinterface.com/downloads/info9974-oUF_Caellian.html
]]

--Application object
cRefactored = {

    setCurrentStyle = function(style) 
    
        cRefactored.currentStyle = style
        
        --If the currentStyle inherits from another, set the metatable for it and unitStyles
        if style.inheritStyle and cRefactored.styles[style.inheritStyle] then
            
            local inheritStyle = cRefactored.styles[style.inheritStyle]
            
            local styleMeta = getmetatable(style) or getmetatable(setmetatable(style,{}))
            styleMeta.__index = inheritStyle
        
            if not style.unitStyles then style.unitStyles = {} end
            local unitStylesMeta = getmetatable(style.unitStyles) or getmetatable(setmetatable(style.unitStyles,{}))
            unitStylesMeta.__index = inheritStyle.unitStyles
        
        end
    
    end,
    
    getCurrentStyle = function() return cRefactored.currentStyle end,
    
    --Constants
    FORWARD = true,
    REVERSE = false,
    
    RIGHT = 0,
    LEFT = 1,
    TOP = 3,
    BOTTOM = 4,
    
    BUFFS = "Buffs",
    DEBUFFS = "Debuffs",
    
    VERTICAL = 'VERTICAL',
    HORIZONTAL = 'HORIZONTAL'
    
}

--Load environment from cRefactored
for k,v in pairs(cRefactored) do getfenv()[k] = v end

--Presentation setup
local noAuras = false -- true to Disable oUF buffs/debuffs and Enable default
local threatWarning = true -- false to Disable the threat warnings

if noAuras == false then
    BuffFrame:Hide()
    TemporaryEnchantFrame:Hide()
end

function cRefactored. hideAuras(self)
    self.Buffs:Hide()
    self.Debuffs:Hide()
end

local backdrop = {
    bgFile = [=[Interface\Tooltips\UI-Tooltip-Background]=],
    insets = {top = -1, left = -1, bottom = -1, right = -1},
}

local normTex = [=[Interface\Addons\oUF_cRefactored\media\normTex]=]
local revTex = [=[Interface\Addons\oUF_cRefactored\media\revTex]=]
local bubbleTex = [=[Interface\Addons\oUF_cRefactored\media\bubbleTex]=]
local buttonTex = [=[Interface\Addons\oUF_cRefactored\media\buttonTex]=]
local font = [=[Interface\AddOns\oUF_cRefactored\media\neuropol x cd bd.ttf]=]

local _, class = UnitClass('player')

local runeloadcolors = {
    [1] = {0.69, 0.31, 0.31},
    [2] = {0.69, 0.31, 0.31},
    [3] = {0.33, 0.59, 0.33},
    [4] = {0.33, 0.59, 0.33},
    [5] = {0.31, 0.45, 0.63},
    [6] = {0.31, 0.45, 0.63},
}

local colors = setmetatable({
    power = setmetatable({
        ['MANA'] = {0.31, 0.45, 0.63},
        ['RAGE'] = {0.69, 0.31, 0.31},
        ['FOCUS'] = {0.71, 0.43, 0.27},
        ['ENERGY'] = {0.65, 0.63, 0.35},
    }, {__index = oUF.colors.power}),
    runes = setmetatable({
        [1] = {0.69, 0.31, 0.31},
        [2] = {0.33, 0.59, 0.33},
        [3] = {0.31, 0.45, 0.63},
        [4] = {0.84, 0.75, 0.65},
    }, {__index = oUF.colors.runes}),
}, {__index = oUF.colors})

oUF.colors.tapped = {0.55, 0.57, 0.61}
oUF.colors.disconnected = {0.84, 0.75, 0.65}

oUF.colors.smooth = {0.69, 0.31, 0.31, 0.65, 0.63, 0.35, 0.15, 0.15, 0.15}

--Our own more sensicle debuff highlight color table
oUF.colors.debuffHighlight = {
    Disease = {0.6,0.4,0,1},
    Poison = {0,0.6,0,1},
    Curse = {0.6,0,1,1},
    Magic = {0.2,0.6,1,1}
}

--Utilities
local function setPoints(target, parent, position, adjustmentHook, noClear)

    --If we haven't been told not to clear existing points, do so
    if not noClear then target:ClearAllPoints() end

    --If setPoints is a list of setPoints, call us with each
    if type(position[1]) == 'table' then
        for i,setPoint in ipairs(position) do
            setPoints(target, parent, setPoint, adjustmentHook, true)
        end 
    else
        if position[2]==nil then
            position = {unpack(position)}
            position[2] = parent
        end
        if adjustmentHook then
            position = adjustmentHook(target, parent, position)
        end
        target:SetPoint(unpack(position))
    end

end

local SetFontString = function(parent, fontName, fontHeight, fontStyle)
    local fs = parent:CreateFontString(nil, 'OVERLAY')
    fs:SetFont(fontName, fontHeight, fontStyle)
    fs:SetJustifyH('LEFT')
    fs:SetShadowColor(0, 0, 0)
    fs:SetShadowOffset(1.25, -1.25)
    return fs
end

local function ShortValue(value)
    if value >= 1e6 then
            return ('%.1fm'):format(value / 1e6):gsub('%.?0+([km])$', '%1')
    elseif value >= 1e3 or value <= -1e3 then
            return ('%.1fk'):format(value / 1e3):gsub('%.?0+([km])$', '%1')
    else
            return value
    end
end

local function GetColor(unit)
    local r, g, b
    
    if (UnitIsDead(unit) or UnitIsGhost(unit) or not UnitIsConnected(unit)) then
        r, g, b = colors.disconnected
    elseif (UnitIsTapped(unit) and not UnitIsTappedByPlayer(unit)) then
        r, g, b = colors.tapped
    elseif (unit == 'player' or unit == 'pet') then
        local num, str = UnitPowerType(unit)
        r, g, b = colors.power[str]
    elseif (UnitIsPlayer(unit)) then
        local _, class = UnitClass(unit)
        r, g, b = colors.class[class]
    else
        local reaction = UnitReaction(unit, 'player')
        r, g, b = FACTION_BAR_COLORS[reaction]
    end

    if (type(r) == 'table') then
        if (r.r) then r, g, b = r.r, r.g, r.b else r, g, b = unpack(r) end
    end

    if (not r or not g or not b) then
        r, g, b = 0.84, 0.75, 0.65
    end

    return r, g, b
end

--Handlers
local Menu = function(self)
    local unit = self.unit:gsub('(.)', string.upper, 1) 
    if (_G[unit..'FrameDropDown']) then
        ToggleDropDownMenu(1, nil, _G[unit..'FrameDropDown'], 'cursor')
    elseif (self.unit:match('party')) then
        ToggleDropDownMenu(1, nil, _G['PartyMemberFrame'..self.id..'DropDown'], 'cursor')
    else
        FriendsDropDown.unit = self.unit
        FriendsDropDown.id = self.id
        FriendsDropDown.initialize = RaidFrameDropDown_Initialize
        ToggleDropDownMenu(1, nil, FriendsDropDown, 'cursor')
    end
end

local function ColorThreat(self, event, unit)   
    --[[
    oUF_Caellian r60 implementation, this is a departure from r61 (not sure 
    what he was smoking there).
    
    Regarding slow updates:  this implementation is fine, but the WoWAPI can be 
    very slow to report threat for party members.
    ]]--
    
    if(self.unit ~= unit) then return end
    local status = UnitThreatSituation(self.unit)
    
    if status and (status > 0) then
            r, g, b = GetThreatStatusColor(status)
            self.FrameBackdrop:SetBackdropBorderColor(r, g, b)
    else
            self.FrameBackdrop:SetBackdropBorderColor(0, 0, 0)
    end
end

local function PostNamePosition(self)
    self.Info:ClearAllPoints()
    if self.Power.value:GetText() then
            self.Info:SetPoint('CENTER', 0, 1)
    else
            self.Info:SetPoint('LEFT', 1, 1)
    end
end

local function HidePortrait(self, unit)
    if self.unit == 'target' then
        if not UnitExists(self.unit) or not UnitIsConnected(self.unit) or not UnitIsVisible(self.unit) then
            self.Portrait:SetAlpha(0)
        else
            self.Portrait:SetAlpha(1)
        end
    end
end

local PostUpdateHealth = function(self, event, unit, bar, min, max)
    if (not UnitIsConnected(unit)) then
        bar:SetValue(0)
        bar.value:SetText('|cffD7BEA5'..'Off'..'|r')
    elseif (UnitIsDead(unit)) then
        bar.value:SetText('|cffD7BEA5'..'Dead'..'|r')
    elseif (UnitIsGhost(unit)) then
        bar.value:SetText('|cffD7BEA5'..'Ghost'..'|r')
    else
        if (min ~= max) then
            local r, g, b
            r, g, b = oUF.ColorGradient(min/max, 0.69, 0.31, 0.31, 0.65, 0.63, 0.35, 0.33, 0.59, 0.33)
            if (unit == 'player') then
                bar.value:SetFormattedText('|cffAF5050%d|r |cffD7BEA5-|r |cff%02x%02x%02x%d%%|r', min, r * 255, g * 255, b * 255, floor(min / max * 100))
            elseif (unit == 'target') then
                bar.value:SetFormattedText('|cffAF5050%s|r |cffD7BEA5-|r |cff%02x%02x%02x%d%%|r', ShortValue(min), r * 255, g * 255, b * 255, floor(min / max * 100))
            else
                bar.value:SetFormattedText('|cff%02x%02x%02x%d%%|r', r * 255, g * 255, b * 255, floor(min / max * 100))
            end
        else
            if (unit ~= 'player' and unit ~= 'pet') then
                bar.value:SetText('|cff559655'..ShortValue(max)..'|r')
            else
                bar.value:SetText('|cff559655'..max..'|r')
            end
        end
    end

    ColorThreat(self, event, unit)
end

local PostUpdatePower = function(self, event, unit, bar, min, max)
    local r, g, b = GetColor(unit)

    bar:SetStatusBarColor(r, g, b)
    bar.bg:SetVertexColor(r * 0.25, g * 0.25, b * 0.25)

    if (self.unit ~= 'player' and self.unit ~= 'target') then return end

    if (min == 0) then
        bar.value:SetText()
    elseif (not UnitIsPlayer(unit) or not UnitIsConnected(unit)) then
        bar.value:SetText()
    elseif (UnitIsDead(unit) or UnitIsGhost(unit)) then
        bar.value:SetText()
    elseif (min == max and UnitPowerType(unit) == 3) then
        bar.value:SetText()
    else
        if (min ~= max) then
            if (UnitPowerType(unit) == 0) then
                if (unit == 'target') then
                    bar.value:SetFormattedText('%d%% |cffD7BEA5-|r %s', floor(min / max * 100), ShortValue(max - (max - min)))
                else
                    bar.value:SetFormattedText('%d%% |cffD7BEA5-|r %d', floor(min / max * 100), max - (max - min))
                end
            else
                bar.value:SetText(max - (max - min))
            end
        else
            if (unit == 'target') then
                bar.value:SetText(ShortValue(min))
            else
                bar.value:SetText(min)
            end
        end
        local num, str = UnitPowerType(unit)
        local color = colors.power[str]
        if (color) then bar.value:SetTextColor(color[1], color[2], color[3]) end
    end
    if (self.unit == 'target') then PostNamePosition(self) end
end

local function OverrideCastbarTime(self, duration)
    if self.channeling then
            self.Time:SetFormattedText('%.1f ', duration)
    elseif self.casting then
            self.Time:SetFormattedText('%.1f ', self.max - duration)
    end
end

local function PostUpdateReputation(self, event, unit, bar)
    local name, id = GetWatchedFactionInfo()
    bar:SetStatusBarColor(FACTION_BAR_COLORS[id].r, FACTION_BAR_COLORS[id].g, FACTION_BAR_COLORS[id].b)
end

local function CancelAura(self, button)
    if (button == 'RightButton' and not self.debuff) then
        CancelUnitBuff('player', self:GetID())
    end
end

local function PostCreateAuraIcon(self, button, icons, index, isDebuff)
	button.backdrop = CreateFrame('Frame', nil, button)
	button.backdrop:SetPoint('TOPLEFT', button, 'TOPLEFT', -3.5, 3)
	button.backdrop:SetPoint('BOTTOMRIGHT', button, 'BOTTOMRIGHT', 4, -3.5)
	button.backdrop:SetFrameStrata('BACKGROUND')
	button.backdrop:SetBackdrop({
		edgeFile = [=[Interface\AddOns\oUF_Caellian\media\glowTex]=], edgeSize = 5,
		insets = {left = 3, right = 3, top = 3, bottom = 3}
	})
	button.backdrop:SetBackdropColor(0, 0, 0)
	button.backdrop:SetBackdropBorderColor(0, 0, 0)

	button.count:SetPoint('BOTTOMRIGHT', button, 'BOTTOMRIGHT', 4, -2)
	button.count:SetJustifyH('CENTER')
	button.count:SetFont(font, 12, 'OUTLINE')
	button.count:SetTextColor(0.84, 0.75, 0.65)

	button.cd:SetReverse()
	button.cd.noOCC = true
	button.cd.noCooldownCount = true

	button.overlay:SetTexture(buttonTex)
	button.overlay:SetTexCoord(0, 1, 0, 1)
	button.overlay.Hide = function(self) end

	if (self.unit == 'player') then
            button:SetScript('OnMouseUp', CancelAura)
	end
end

local function PostUpdateAuraIcon(self, icons, unit, icon, index, offset, filter, isDebuff)
	if (self.unit == 'target' and icon.isPlayer) then
            if (isDebuff) then
                icon.overlay:SetVertexColor(0.69, 0.31, 0.31)
            else
                icon.overlay:SetVertexColor(0.33, 0.59, 0.33)
            end
	elseif (self.unit == 'player' and icon.isPlayer and not isDebuff) then
            icon.overlay:SetVertexColor(0.33, 0.59, 0.33)
	else
            icon.overlay:SetVertexColor(0.25, 0.25, 0.25)
	end

	if (isDebuff and UnitIsEnemy('player', unit)) then
            local _, _, _, _, _, duration = UnitAura(unit, index, filter)
            if (duration and duration > 0 and not icon.isPlayer) then
                icon.cd:Hide()
            end
	end
end

--Formatters
local function formatHealth(self, bottomSetPoint, valueSetPoint, fontSize)

    self.Health = CreateFrame('StatusBar', nil, self)
    self.Health:SetFrameLevel(self.frameLevel)
    
    self.Health:SetPoint('TOPLEFT')
    self.Health:SetPoint(unpack(bottomSetPoint))
    self.Health:SetStatusBarTexture(self.direction and normTex or revTex)

    self.Health.colorTapping = true
    self.Health.colorDisconnected = true
    self.Health.colorSmooth = true
    self.Health.frequentUpdates = true
    
    self.Health.bg = self.Health:CreateTexture(nil, 'BORDER')
    self.Health.bg:SetTexture(self.direction and normTex or revTex)
    self.Health.bg:SetAllPoints(self.Health)
    self.Health.bg.multiplier = 0.33

    self.Health.value = SetFontString(self.Health, font, fontSize)
    self.Health.value:SetPoint(unpack(valueSetPoint))
    
end

local function formatPower(self, topSetPoint)
    self.Power = CreateFrame('StatusBar', nil, self)
    self.Power:SetStatusBarTexture(self.direction and normTex or revTex)
    self.Power:SetPoint(unpack(topSetPoint))
    self.Power:SetPoint('BOTTOMRIGHT')
    self.Power.frequentUpdates = true
    self.Power.bg = self.Power:CreateTexture(nil, 'BORDER')
    self.Power.bg:SetTexture(self.direction and normTex or revTex)
    self.Power.bg:SetAllPoints(self.Power)
    self.Power.value = SetFontString(self.Health, font, 11)
    self.Power.value:SetPoint('LEFT', 1, 1)
end

local function formatExperience(self, setPoint)

    if IsAddOnLoaded('oUF_Experience') then
        self.Experience = CreateFrame('StatusBar', nil, self)
        self.Experience:SetPoint('TOPLEFT', self.Health, 'TOPLEFT', 0, 7.5)
        self.Experience:SetStatusBarTexture(normTex)
        self.Experience:SetStatusBarColor(0.55, 0.57, 0.61)
        self.Experience:SetBackdrop(backdrop)
        self.Experience:SetBackdropColor(0.15, 0.15, 0.15)
        self.Experience:SetPoint(unpack(setPoint))

        self.Experience.bg = self.Experience:CreateTexture(nil, 'BORDER')
        self.Experience.bg:SetAllPoints(self.Experience)
        self.Experience.bg:SetTexture(normTex)
        self.Experience.bg:SetVertexColor(0.15, 0.15, 0.15)

        self.Experience.MouseOver = true
        self.Experience.Tooltip = true
    end
    
end

local function formatPortrait(self)

    --[[
    Point setting here is a slight departure from oUF_Caellian r61.
    The discussion on the forums suggests that positioning these elements to
    avoid pixel gaps is not a generally solvable proble across UI scales and
    resolutions, but these settings have been robust across all of my test 
    cases.
    ]]--
    
    self.Portrait = CreateFrame('PlayerModel', nil, self)
    self.Portrait:SetFrameLevel(1)
    self.Portrait:SetPoint('TOPLEFT', self.Health, 'BOTTOMLEFT')
    self.Portrait:SetPoint('BOTTOMRIGHT', self.Power, 'TOPRIGHT')
    table.insert(self.__elements, HidePortrait)

    self.PortraitOverlay = CreateFrame('StatusBar', nil, self)
    self.PortraitOverlay:SetFrameLevel(2)
    self.PortraitOverlay:SetPoint('TOPLEFT', self.Portrait, 'TOPLEFT', -1, 1)
    self.PortraitOverlay:SetPoint('BOTTOMRIGHT', self.Portrait, 'BOTTOMRIGHT')
    self.PortraitOverlay:SetStatusBarTexture(unit == 'player' and normTex or revTex)
    self.PortraitOverlay:SetStatusBarColor(.15,.15,.15,.75)
    
    self.CombatFeedbackText = SetFontString(self.PortraitOverlay, font, 18, 'OUTLINE')
    self.CombatFeedbackText:SetPoint('CENTER', 0, 1)

end

local function formatStatus(self)
    self.Status = SetFontString(self.PortraitOverlay, font, 18, 'OUTLINE')
    self.Status:SetPoint('CENTER', 0, 1)
    self.Status:SetTextColor(0.69, 0.31, 0.31, 0)
    self:Tag(self.Status, '[pvp]')
end

local function formatCastbar(self, showInfo, backdrop, setPoint)
		
    self.Castbar = CreateFrame('StatusBar', nil, self)
    self.Castbar:SetStatusBarTexture(self.direction and normTex or revTex)
    self.Castbar:SetStatusBarColor(0.55, 0.57, 0.61, 0.5)
    setPoints(self.Castbar, self.Health, setPoint)
    
    if backdrop then
        self.Castbar:SetBackdrop(backdrop)
        self.Castbar:SetBackdropColor(0.15, 0.15, 0.15)
    else
        self.Castbar:SetFrameLevel(3)
    end
    
    self.Castbar.bg = self.Castbar:CreateTexture(nil, 'BORDER')
    self.Castbar.bg:SetAllPoints(self.Castbar)
    self.Castbar.bg:SetTexture(self.direction and normTex or revTex)
    self.Castbar.bg:SetVertexColor(0.15, 0.15, 0.15, 0.0)

    if showInfo then
        self.Castbar.Text = SetFontString(self.Castbar, font, 11)
        self.Castbar.Text:SetPoint('LEFT', 1, 1)
        self.Castbar.Text:SetTextColor(0.84, 0.75, 0.65)

        self.Castbar.Time = SetFontString(self.Castbar, font, 11)
        self.Castbar.Time:SetPoint('RIGHT', -1, 1)
        self.Castbar.Time:SetTextColor(0.84, 0.75, 0.65)
        self.Castbar.Time:SetJustifyH('RIGHT')
        self.Castbar.CustomTimeText = OverrideCastbarTime

        self.Castbar.Icon = self.Castbar:CreateTexture(nil, 'ARTWORK')
        self.Castbar.Icon:SetHeight(22.5)
        self.Castbar.Icon:SetWidth(22.5)
        self.Castbar.Icon:SetTexCoord(0,1,0,1)
        self.Castbar.Icon:SetPoint(unpack(self.direction and {'RIGHT', 32.5, 0} or {'LEFT', -32.5, 0}))

        self.IconOverlay = self.Castbar:CreateTexture(nil, 'OVERLAY')
        self.IconOverlay:SetPoint('TOPLEFT', self.Castbar.Icon, 'TOPLEFT', -1.5, 0.5)
        self.IconOverlay:SetPoint('BOTTOMRIGHT', self.Castbar.Icon, 'BOTTOMRIGHT', 1.5, -0.5)
        self.IconOverlay:SetTexture(buttonTex)
        self.IconOverlay:SetVertexColor(0.25, 0.25, 0.25)

        self.IconBackdrop = CreateFrame('Frame', nil, self)
        self.IconBackdrop:SetPoint(unpack(self.direction and {'TOPLEFT', self.Castbar, 'TOPRIGHT', 4, 4} or {'TOPRIGHT', self.Castbar, 'TOPLEFT', -4, 4}))
        self.IconBackdrop:SetPoint(unpack(self.direction and {'BOTTOMRIGHT', self.Castbar, 'BOTTOMRIGHT', 38.5, -5} or {'BOTTOMLEFT', self.Castbar, 'BOTTOMLEFT', -38.5, -5}))
        self.IconBackdrop:SetFrameStrata('BACKGROUND')
        self.IconBackdrop:SetParent(self.Castbar)
        self.IconBackdrop:SetBackdrop({
          edgeFile = [=[Interface\AddOns\oUF_Caellian\media\glowTex]=], edgeSize = 5,
          insets = {left = 3, right = 3, top = 3, bottom = 3}
        })
        self.IconBackdrop:SetBackdropColor(0, 0, 0)
        self.IconBackdrop:SetBackdropBorderColor(0, 0, 0)
    end
    
end

local function formatRaidIcon(self, setPoint, height, width)
    self.RaidIcon = self.Health:CreateTexture(nil, 'OVERLAY')
    self.RaidIcon:SetHeight(height)
    self.RaidIcon:SetWidth(width)
    self.RaidIcon:SetPoint(unpack(setPoint))
end

local function formatHighlight(self)
    self.Highlight = self:CreateTexture(nil, 'HIGHLIGHT')
    self.Highlight:SetTexture(self.direction and normTex or revTex)
    self.Highlight:SetAllPoints(self.Health)
    self.Highlight:SetBlendMode('ADD')
    self.Highlight:SetAlpha(0.25)
end

local function formatFrameBackdrop(self)
    self.FrameBackdrop = CreateFrame('Frame', nil, self)
    self.FrameBackdrop:SetPoint('TOPLEFT', self, 'TOPLEFT', -4.5, 4)
    self.FrameBackdrop:SetFrameStrata('BACKGROUND')
    self.FrameBackdrop:SetBackdrop({
      edgeFile = [=[Interface\AddOns\oUF_Caellian\media\glowTex]=], edgeSize = 5,
      insets = {left = 3, right = 3, top = 3, bottom = 3}
    })
    self.FrameBackdrop:SetBackdropColor(0, 0, 0)
    self.FrameBackdrop:SetBackdropBorderColor(0, 0, 0)
    self.FrameBackdrop:SetPoint('BOTTOMRIGHT', self, 'BOTTOMRIGHT', 4.5, -4.5)
end

--Constitutive decorators
function cRefactored.setFrameLevel(self, unit)
    if unit then self.frameLevel = 1
    elseif self:GetAttribute('unitsuffix') then self.frameLevel = 3
    else self.frameLevel = 2
    end
end

function cRefactored.style(self) 
	
    self.menu = Menu
    self.colors = colors
    self:SetMovable(true)
    self:EnableMouse(true)
    self:RegisterForClicks('AnyUp')
    self:SetAttribute('type2', 'menu')

    self:SetScript('OnEnter', UnitFrame_OnEnter)
    self:SetScript('OnLeave', UnitFrame_OnLeave)

    --self:SetScript('OnMouseDown', function(self) if(IsAltKeyDown()) then self:StartMoving() end end)
    --self:SetScript('OnMouseUp', function(self) self:StopMovingOrSizing() end)

    self:SetBackdrop(backdrop)
    self:SetBackdropColor(0, 0, 0)

    self.ignoreHealComm = true
    self.disallowVehicleSwap = true

    self:RegisterEvent('UNIT_THREAT_SITUATION_UPDATE', ColorThreat)
    
    local AggroSelect = function() if UnitExists('target') then PlaySound('igCreatureAggroSelect') end end
    self:RegisterEvent('PLAYER_TARGET_CHANGED', AggroSelect)

    self.PostUpdateHealth = PostUpdateHealth
    self.PostUpdatePower = PostUpdatePower
    self.PostCreateAuraIcon = PostCreateAuraIcon
    self.PostUpdateAuraIcon = PostUpdateAuraIcon    

    self.direction = FORWARD

end

--General decorators
function cRefactored.infoText(self)
    self.Info = SetFontString(self.Health, font, self.infoText.fontSize)
    self.Info:SetPoint(unpack(self.infoText.setPoint))
    self:Tag(self.Info, self.infoText.tag)
end

function cRefactored.miniFrameStyle(self)
    --UnitPets
    formatHealth(self, {'BOTTOMRIGHT'}, {'BOTTOMRIGHT', -1, 2}, 9)
    
    self.infoText = {fontSize=9, setPoint={'TOPLEFT',1,0}, tag='[colorfeed][shortsmall]'}
    
    self:SetAttribute('initial-height', 10)
    self:SetAttribute('initial-width', 109.5)
    
    formatHighlight(self)
    formatFrameBackdrop(self)
    
end

function cRefactored.smallFrameStyle(self)
    --RaidUnits
    formatHealth(self, {'BOTTOMRIGHT', 0, 6}, {'BOTTOMRIGHT', -1, 2}, 9)
    self.infoText = {fontSize=9, setPoint={'TOPLEFT',1,0}, tag='[colorfeed][shortmedium]'}
    formatPower(self, {'TOPLEFT', self.Health, 'BOTTOMLEFT', 0, -1})
    
    self:SetAttribute('initial-height', 30)
    self:SetAttribute('initial-width', 60)
    
    formatRaidIcon(self, {'BOTTOMLEFT', 1, 2}, 10, 10)
    formatHighlight(self)
    formatFrameBackdrop(self)
    
end

function cRefactored.mediumFrameStyle(self)
    --TargetTarget, Focus, FocusTarget, Pet, Party
    formatHealth(self, {'BOTTOMRIGHT', 0, 6},{'RIGHT', -1, 1}, 9)
    self.infoText = {fontSize=9, setPoint={'LEFT',1,1}, tag='[colorfeed][shortmedium]'}
    formatPower(self, {'TOPLEFT', self.Health, 'BOTTOMLEFT', 0, -1})
    
    formatCastbar(self, false, backdrop, {{'TOPLEFT', self.Health, 'TOPLEFT', 0, 9},{'BOTTOMRIGHT', self.Health, 'TOPRIGHT', 0, 4}})
    
    self:SetAttribute('initial-height', 22)
    self:SetAttribute('initial-width', 109.5)
    
    formatRaidIcon(self, {'TOP', 0, 8}, 14, 14)
    formatHighlight(self)
    formatFrameBackdrop(self)
    
end

function cRefactored.largeFrameStyle(self)
    --Player and Target
    self.infoText = {fontSize=11, setPoint={'LEFT',1,1}, tag='[colorfeed][shortlarge] [diffcolor][level] [shortclassification]'}

    formatHealth(self, {'BOTTOMRIGHT', 0, 31}, {'RIGHT', -1, 1}, 11)
    formatPower(self, {'TOPLEFT', self.Health, 'BOTTOMLEFT', 0, -24})

    if noAuras == true then hideAuras(self) end

    formatPortrait(self)
    formatStatus(self)
    formatCastbar(self, true, false, {{'TOPLEFT', self.Health, 'BOTTOMLEFT', 0, 0},{'BOTTOMRIGHT', self.Power, 'TOPRIGHT', 0, 0}})
    
    self:SetScript('OnEnter', function(self) self.Status:SetAlpha(.25); UnitFrame_OnEnter(self) end)
    self:SetScript('OnLeave', function(self) self.Status:SetAlpha(0); UnitFrame_OnLeave(self) end)

    self:SetAttribute('initial-height', 53)
    self:SetAttribute('initial-width', 230)

    formatRaidIcon(self, {'TOP', 0, 8}, 14, 14)
    formatHighlight(self)
    formatFrameBackdrop(self)

end

function cRefactored.leaderIcon(self)
    self.Leader = self.Health:CreateTexture(nil, 'OVERLAY')
    self.Leader:SetHeight(14)
    self.Leader:SetWidth(14)
    self.Leader:SetPoint('TOPLEFT', 0, 10)
end

function cRefactored.masterLooterIcon(self)
    self.MasterLooter = self.Health:CreateTexture(nil, 'OVERLAY')
    self.MasterLooter:SetHeight(14)
    self.MasterLooter:SetWidth(14)
    self.MasterLooter:SetPoint('TOPRIGHT', 0, 10)
end

function cRefactored.mainAssistIcon(self)
    self.MainAssist = self.Health:CreateTexture(nil, 'OVERLAY')
    self.MainAssist:SetHeight(14)
    self.MainAssist:SetWidth(14)
    self.MainAssist:SetPoint('TOPLEFT', 15, 10)
end

function cRefactored.mainTankIcon(self)
    self.MainTank = self.Health:CreateTexture(nil, 'OVERLAY')
    self.MainTank:SetHeight(14)
    self.MainTank:SetWidth(14)
    self.MainTank:SetPoint('TOPLEFT', 15, 10)
end

function cRefactored.range(self)
    self.outsideRangeAlpha = 0.5
    self.inRangeAlpha = 1
    self.Range = true
end

function cRefactored.friendlyRange(self)
    self.outsideRangeAlpha = 0.5
    self.inRangeAlpha = 1
    self.FriendlyRange = true
end

function cRefactored.barFade(self) self.BarFade = true end

function cRefactored.runeBar(self, unit)
    if IsAddOnLoaded('oUF_RuneBar') then
        self.RuneBar = {}
        for i = 1, 6 do
            self.RuneBar[i] = CreateFrame('StatusBar', nil, self)
            if(i == 1) then
                self.RuneBar[i]:SetPoint('TOPLEFT', self, 'BOTTOMLEFT', 0, -1)
            else
                self.RuneBar[i]:SetPoint('TOPLEFT', self.RuneBar[i-1], 'TOPRIGHT', 1, 0)
            end
            self.RuneBar[i]:SetHeight(7)
            self.RuneBar[i]:SetWidth(230/6 - 0.85)
            self.RuneBar[i]:SetStatusBarTexture(normTex)
            self.RuneBar[i]:SetStatusBarColor(unpack(runeloadcolors[i]))
            self.RuneBar[i]:SetBackdrop(backdrop)
            self.RuneBar[i]:SetBackdropColor(0.15, 0.15, 0.15)
            self.RuneBar[i]:SetMinMaxValues(0, 1)

            self.RuneBar[i].bg = self.RuneBar[i]:CreateTexture(nil, 'BORDER')
            self.RuneBar[i].bg:SetAllPoints(self.RuneBar[i])
            self.RuneBar[i].bg:SetTexture(normTex)
            self.RuneBar[i].bg:SetVertexColor(0.15, 0.15, 0.15)
        end
    
        self.Debuffs:ClearAllPoints()
        self.Debuffs:SetPoint('TOPLEFT', self, 'BOTTOMLEFT', -1, -15)
    
        if self.FrameBackdrop then
            self.FrameBackdrop:SetPoint('BOTTOMRIGHT', self, 'BOTTOMRIGHT', 4.5, -12)
        end
    end
end

--Unit-specific decorators
function cRefactored.playerFrame(self)
    
    --In combat Icon
    self.Combat = self.Health:CreateTexture(nil, 'OVERLAY')
    self.Combat:SetHeight(12)
    self.Combat:SetWidth(12)
    self.Combat:SetPoint('CENTER')
    self.Combat:SetTexture(bubbleTex)
    self.Combat:SetVertexColor(0.69, 0.31, 0.31)

    --Resting Icon
    if UnitLevel('player') ~= MAX_PLAYER_LEVEL then
        self.Resting = self.Power:CreateTexture(nil, 'OVERLAY')
        self.Resting:SetHeight(18)
        self.Resting:SetWidth(18)
        self.Resting:SetPoint('BOTTOMLEFT', -8.5, -8.5)
        self.Resting:SetTexture([=[Interface\CharacterFrame\UI-StateIcon]=])
        self.Resting:SetTexCoord(0,0.5,0,0.421875)
    end

    --Setup for other oUF plugins
    if IsAddOnLoaded('oUF_Reputation') then
        self.Reputation = CreateFrame('StatusBar', nil, self)
        self.Reputation:SetPoint('TOPLEFT', self.Health, 'TOP', 5.5, 9)
        self.Reputation:SetPoint('BOTTOMRIGHT', self.Health, 'TOPRIGHT', 0, 4)
        self.Reputation:SetStatusBarTexture(normTex)
        self.Reputation:SetBackdrop(backdrop)
        self.Reputation:SetBackdropColor(0.15, 0.15, 0.15)

        self.Reputation.bg = self.Reputation:CreateTexture(nil, 'BORDER')
        self.Reputation.bg:SetAllPoints(self.Reputation)
        self.Reputation.bg:SetTexture(normTex)
        self.Reputation.bg:SetVertexColor(0.15, 0.15, 0.15)

        self.Reputation.PostUpdate = PostUpdateReputation
        self.Reputation.MouseOver = true
        self.Reputation.Tooltip = true
    end

    if IsAddOnLoaded('oUF_Swing') then
        self.Swing = CreateFrame('StatusBar', nil, self)
        self.Swing:SetPoint('TOPLEFT', self.Health, 'TOPLEFT', 0, 51.5)
        self.Swing:SetPoint('BOTTOMRIGHT', self.Health, 'TOPRIGHT', 0, 46.5)
        self.Swing:SetStatusBarTexture(normTex)
        self.Swing:SetStatusBarColor(0.55, 0.57, 0.61)
        self.Swing:SetBackdrop(backdrop)
        self.Swing:SetBackdropColor(0.15, 0.15, 0.15)

        self.Swing.bg = self.Swing:CreateTexture(nil, 'BORDER')
        self.Swing.bg:SetAllPoints(self.Swing)
        self.Swing.bg:SetTexture(normTex)
        self.Swing.bg:SetVertexColor(0.15, 0.15, 0.15)
    end
    
    formatExperience(self, {'BOTTOMRIGHT', self.Health, 'TOP', -5.5, 4})

    --Add SafeZone to casting bar (accounts for latency)
    if self.Castbar then
        self.Castbar.SafeZone = self.Castbar:CreateTexture(nil,'ARTWORK')
        self.Castbar.SafeZone:SetTexture(normTex)
        self.Castbar.SafeZone:SetVertexColor(0.69, 0.31, 0.31, 0.75)
    end
    
end

function cRefactored.targetFrame(self, unit)

    --Combo points
    self.CPoints = {}
    self.CPoints.unit = 'player'

    for i = 1, 5 do
        self.CPoints[i] = self.Power:CreateTexture(nil, 'OVERLAY')
        self.CPoints[i]:SetHeight(12)
        self.CPoints[i]:SetWidth(12)
        self.CPoints[i]:SetTexture(bubbleTex)
        if i == 1 then
            self.CPoints[i]:SetPoint('LEFT')
            self.CPoints[i]:SetVertexColor(0.69, 0.31, 0.31)
        else
            self.CPoints[i]:SetPoint('LEFT', self.CPoints[i-1], 'RIGHT', 1)
        end
    end
    
    self.CPoints[2]:SetVertexColor(0.69, 0.31, 0.31)
    self.CPoints[3]:SetVertexColor(0.65, 0.63, 0.35)
    self.CPoints[4]:SetVertexColor(0.65, 0.63, 0.35)
    self.CPoints[5]:SetVertexColor(0.33, 0.59, 0.33)

end

function cRefactored.petFrame(self, unit)
    self.Auras = CreateFrame('Frame', nil, self)
    self.Auras:SetHeight(24)
    self.Auras:SetWidth(24 * 8)
    self.Auras:SetPoint('TOPRIGHT', self, 'TOPLEFT', -9, 1)
    self.Auras.size = 24
    self.Auras.spacing = 1
    self.Auras.numBuffs = 8
    self.Auras.numDebuffs = 8
    self.Auras.gap = true
    self.Auras.initialAnchor = 'TOPRIGHT'
    self.Auras['growth-x'] = 'LEFT'

    self.CPoints = SetFontString(self.Power, font, 10, 'OUTLINE')
    self.CPoints:SetPoint('TOP', 0, 3.5)
    self.CPoints:SetTextColor(0.84, 0.75, 0.65)
    self:Tag(CPoints, '[cpoints]')
    
    formatExperience(self, {'BOTTOMRIGHT', self.Health, 'TOPRIGHT', 0, 4})
end

--Functions that return simple decorators
function cRefactored.setDirection(direction)
    --MUST be called before other decs
    --'forward' for left-to-right orientation; 'reverse' the opposite
    
    return function(self, unit)
        self.direction = direction
    end    
end

function cRefactored.createBuffs(elementType, setPoint, orientation, height, width, size, filter, onlyShowPlayer)
    
    local growth_x, growth_y, initialAnchor
    if orientation == RIGHT then
        growth_x = 'RIGHT'
        growth_y = 'DOWN'
        initialAnchor = 'TOPLEFT'
    elseif orientation == LEFT then
        growth_x = 'LEFT'
        growth_y = 'DOWN'
        initialAnchor = 'TOPRIGHT'
    elseif orientation == BOTTOM then
        growth_x = 'RIGHT'
        growth_y = 'DOWN'
        initialAnchor = 'TOPLEFT'
    else
        growth_x = 'RIGHT'
        growth_y = 'UP'
        initialAnchor = 'BOTTOMLEFT'
    end

    return function(self)
        self[elementType] = CreateFrame('Frame', nil, self)
        self[elementType]:SetHeight(height)
        self[elementType]:SetWidth(width)
        self[elementType].size = size
        self[elementType].spacing = 1
        setPoints(self[elementType], self, setPoint)
        self[elementType].initialAnchor = initialAnchor
        self[elementType]['growth-x'] = growth_x
        self[elementType]['growth-y'] = growth_y
        self[elementType].filter = filter
        self[elementType].onlyShowPlayer = onlyShowPlayer
    end
    
end

function cRefactored.readyCheckIcon(setPoint)
    return function(self)
        self.ReadyCheck = self.Health:CreateTexture(nil, 'OVERLAY')
        self.ReadyCheck:SetHeight(12)
        self.ReadyCheck:SetWidth(12)
        --self.ReadyCheck:SetPoint(unpack(setPoint))
        setPoints(self.ReadyCheck, self, setPoint)
    end
end

--dots and timers plugins (return decorators and handlers)
local function setDotPosition(self, dot, dotTexture, dotIndex, currentDots)
    if dot.setPoint then
        setPoints(dotTexture, self[dot.anchor], dot.setPoint, dot.adjustmentHook)
    
    elseif dot.advancePosition then
        --Find the closest dot of the same type, and set our position relative to it
        for backIdx = 1, (dotIndex-1) do
            
            local referenceDot = currentDots[dotIndex-backIdx]
            if referenceDot.dotType == dot.dotType then
                --Set the points
                setPoints(dotTexture, self['dotTexture'..tostring(dotIndex-backIdx)], dot.advancePosition, dot.adjustmentHook)
        
                --Exit the loop
                return true
        
            end
    
        end
        
        --If we're still here, issue a warning.
        print("Unable to find a reference dot for the advanced", dot.dotType, dot, "on", self)
    
    elseif dot.stackPosition then
        --[[
        Find the closest dot of this type that either has a SetPoint or is
        associated with a shown texture]]
        for backIdx = 1,(dotIndex-1) do

            local referenceDot = currentDots[dotIndex-backIdx]
            local referenceTexture = self['dotTexture'..tostring(dotIndex-backIdx)]
    
            if referenceDot.dotType == dot.dotType then
                if referenceTexture:IsShown() then
                    setPoints(dotTexture, referenceTexture, dot.stackPosition, dot.adjustmentHook)
                    break
                elseif referenceDot.setPoint then
                    setPoints(dotTexture, self[referenceDot.anchor], referenceDot.setPoint, referenceDot.adjustmentHook)
                    break
                end
            end

        end
    
    else
        print("Unable to determine a set position for the", dot.dotType, dot.aura, "on", self.unit)
    end
    
end

local function buildDotHandler(dot)

    --If the dot doesn't have a handler
    if not dot.handler then

        --If there is no handlerType but aura is set, use default OnOff handler
        if (not dot.handlerType) and dot.aura then
            dot.handlerType = dot.defaultHandlerType
        end
    
        --If there is a handler type, set the handler using it
        if dot.handlerType then
            dot.handler = dot.handlerType(dot)
        else
            print("Warning: unable to build a handler for dot:", tostring(dot))
            dot.handler = function() end
        end
    
    end
    
    --If this is a stacked dot or a dot with an adjustmentHook, inject a handler that will update the position at run time
    if dot.adjustmentHook or ((not dot.setPoint) and (not dot.advancePosition) and dot.stackPosition) then
    
        --Save the state setting handler
        local stateHandler = dot.handler
    
        --Hook in a handler that sets the position after updating state
        dot.handler = function(self, unit, texture, event, dotIndex)
    
            local dot = self.dots[dotIndex]
            setDotPosition(self, dot, texture, dotIndex, self.dots)
        
            --Call the state handler
            stateHandler(self, unit, texture, event, dotIndex)
        
        end
    end
    
end

local function initializeDot(dot, config)

    --Set the dot metatable to index the current defaults
    local dotMeta = getmetatable(dot) or getmetatable(setmetatable(dot,{}))
    dotMeta.__index = config

    --Update the handler on the dot if needed now that full configuration is available
    buildDotHandler(dot)
    
    --Flag this dot as initialized so we don't waste time doing it again
    dot.initialized = true
    
end

local function buildTexture(self, dot, dotIndex)
    --Setup the dot texture
    local newDot = self[dot.anchor]:CreateTexture(nil, 'OVERLAY')
    self['dotTexture'..tostring(dotIndex)] = newDot

    newDot:SetTexture(dot.texture)
    newDot:SetVertexColor(unpack(dot.missingColor))
    
    return newDot
end

local function buildStatusBar(self, dot, dotIndex)
    local newDot = CreateFrame('StatusBar', nil, self)
    
    self['dotTexture'..tostring(dotIndex)] = newDot
    newDot:SetOrientation((dot.direction or HORIZONTAL))
    newDot:SetStatusBarTexture(dot.texture)
    newDot:SetStatusBarColor(unpack(dot.barColor))
    
    if dot.backdrop then
        newDot:SetBackdrop(backdrop)
        newDot:SetBackdropColor(0.15, 0.15, 0.15)
    end
    
    return newDot
end

local function buildDots(self, buildElement, currentDots, newDots, config)

    --Find the current dot offset
    local dotOffset = #currentDots
    
    for i,dot in ipairs(newDots) do
        
        local dotIndex = i + dotOffset

        if not dot.initialized then initializeDot(dot, config) end
    
        --Make a new texture, frame, etc. to hold an instance of this dot on this frame
        local newDot = buildElement(self, dot, dotIndex)
    
        --Setup layout
        if dot.height then newDot:SetHeight(dot.height) end
        if dot.width then newDot:SetWidth(dot.width) end
    
        setDotPosition(self, dot, newDot, dotIndex, currentDots)
    
        if dot.blendMode then newDot:SetBlendMode(dot.blendMode) end
    
        --Insert this dot into the currentDots structure
        table.insert(currentDots, dot)
    
    end

end

local function __queryAura(self, aura, filter, queryCall)
    --[[
    aura can be a single spell name or a table of names.
    ]]--

    --[[
    A note about querying auras and this implementaion:

    The current documentation on WoWwiki states that:

    UnitBuff(unit, index or name [, castable])
    UnitDebuff(unitID, index or name [, removable])

    Are aliases for:

    UnitAura("unit", index or ["name", "rank"][, "filter"])

    With filter = "HELPFUL" and "HARMFUL" respectively.  However, this does not 
    actually appear to be the case.  It is likely that this stated behavior was 
    intended, but, as in so many things, the underlying implementation is brittle.

    Althought both types of calls behave similarly when referncing Auras by index,
    UnitAura is inconsistant in its response to calls using spell names.  I have
    observed many instances where:

    UnitDebuff('target', 'Soothe Animal') returns debufInfo
    UnitAura('target', 'Soothe Animal', 'HARMFUL') returns nil

    I have had a similar experience with UnitBuff.
    ]]--

    if type(aura) == 'table' then

        for i,auraName in ipairs(aura) do
            local result = __queryAura(self, auraName, filter, queryCall)
            if result then return result end
        end

    else

        local auraInfo = {queryCall(self.unit, aura, filter)}
        if auraInfo[1] then
            return {name = auraInfo[1], 
                    rank = auraInfo[2], 
                    icon = auraInfo[3], 
                    count = auraInfo[4], 
                    debuffType = auraInfo[5], 
                    duration = auraInfo[6], 
                    expirationTime = auraInfo[7], 
                    unitCaster = auraInfo[8],
                    isMine = auraInfo[8]=='player',
                    isStealable = auraInfo[9]}
        end
    
    end
end
        
function cRefactored.queryAura(self, aura, filter)
    for i,queryCall in ipairs({UnitBuff, UnitDebuff}) do
        local result = __queryAura(self, aura, filter, queryCall)
        if result then return result end
    end
end

--[[
    Dots/timers are a general frame marking system, designed to give us 
    grid-plugin-inspired behaviors with terse syntax in the config table.  Each
    dot manages the state of a texture associated with a frame.  Setting states
    based on the associated unit auras is supported out-of-the-box.  See 
    cRefactored.dotHander* functions below for other dot handler types.

    The dots(...) and timers(...) functions are decorator factories.  Pass each
    an ordered list of dot/timer definitions as many times in a config scheme 
    as you like.

    Each dot/timer definition is a table containing:

        Hander options  Define one and only one of the following (order 
                        represents priority).

        .aura           A string describing an aura to query to set dot state, 
                        or a table of string values listing auras (default
                        handlerType is dotHandlerOnOff for dots and 
                        timerHandlerDuration for timers)
        
        OR
    
        .handlerType    A function(dot) that returns a handler.

        OR
        
        .handler        An update function(self, unit, texture, event) that 
                        should mutate the dot directly to update its state

        Element position        Define one and only one of the following (order
                                represents priority).

        .anchor                 Anchor for setPoint (requires a .setPoint)
        .setPoint               SetPoint table, mutated to be relative to the 
                                anchor (require a .anchor)
        
        OR
    
        .advancePosition        SetPoint table, mutated to be relative to the
                                previous dot.
        
        OR
                
        .stackPosition          Causes position to be dynamically updated based
                                on whether or not the previous dot(s) in the 
                                stack are shown.  A stack is a continuous series 
                                of dots where .stackPosition is not nil up to 
                                the closest dot in the set that has a non nil
                                setPoint.  This value should be in the format of 
                                a SetPoint table.
        
        Note:  .advancePosition and .stackPosition differentiates between dot
        'types' in the unit frame stack (currently 'dots' versus 'timers').

        Visual presentation     Optionally define the following
        .adjustmentHook         Hook function(targetFrame, parentFrame, setPoint)
                                that should return a setPoint which gets called
                                immediately before a dot is repositioned.
    
        .height                 Height of the dot
        .width                  Width of the dot
        .texture                Texture
        .onlyShowPlayer         Only track auras cast by the player
        
        Dots only
        .presentColor           An {R,G,B} color array shown when the dot is on
        .missingColor           An {R,G,B} color array shown when the dot is off

        Timers only
        .barColor               An {R,G,B} color array for the status bar color

    Enablind, disabling and updating dot states is handled by the dots element.
    (see elements/dots.lua)
]]--


local function __addDots(dotType, buildElement, defaultHandlerType, ...)

    local dots = {}
    
    --Setup a handler for each dot
    for dotIdx = 1, select('#', ...) do

        --Get the next dot
        local dot = select(dotIdx,...)
        
        dot.dotType = dotType
        dot.defaultHandlerType = defaultHandlerType
    
        table.insert(dots, dot)
    end
    
    --Return a decorator for this dot
    return function(self, unit, config)
    
        --If this is the first call to a dots decorator as part of this styling session
        if not self.setStyleSession.dots then
            self.dots = {}
            self.setStyleSession.dots = true
        end
        
        buildDots(self, buildElement, self.dots, dots, config[dotType..'sConfig'])
    
    end

end

function cRefactored.dots(...)
    return __addDots('dot', buildTexture, cRefactored.dotHandlerOnOff, ...)
end

function cRefactored.timers(...)
    return __addDots('timer', buildStatusBar, cRefactored.timerHandlerDuration, ...)
end

function cRefactored.dotHandlerOnOff(dot)
    
    return function(self, unit, texture)

        local result = cRefactored.queryAura(self, dot.aura)

        if result then
            --We have the aura
            if not dot.onlyShowPlayer or result.isMine then
                texture:SetVertexColor(unpack(dot.presentColor))
            end

        else
            --We don't have the aura
            texture:SetVertexColor(unpack(dot.missingColor))
        end
    end
    
end

function cRefactored.dotHandlerGnomeHerder(dot)
    return function() print("gnomeHerder not yet implemented") end
end

function cRefactored.dotHandlerDebuffHighlight(dot)
    --dot can define onlyShowRemoveable (onlyShowPlayer is used as an alias)
    --However this argument doesn't appear to be working in UnitDebuff.
    
    return function(self, unit, texture) 
    
        --Iterate through possible debuff positions
        for buffIndex = 1, 40 do
            local name, _, _, _, debuffType  =  UnitDebuff(self.unit, buffIndex, (dot.onlyShowRemoveable or dot.onlyShowPlayer))
        
            --If we've hit the end of the auras break the loop
            if not name then break end
        
            --If aura has a debuffType
            if debuffType then
        
                --See presentation section above for color definitions
                texture:SetVertexColor(unpack(oUF.colors.debuffHighlight[debuffType]))
            
                --Just return, because we don't yet support showing multiple debuffs at once
                return true
            end
        end
    
        --If we're still here, make sure the texture is turned off
        texture:SetVertexColor(unpack(dot.missingColor))    
    end
    
end

function cRefactored.timerHandlerDuration(dot)
    --[[
    Sets the length of the status bar based on the remaining duration for the
    watched aura.  Hides the bar if the frame doesn't have the aura.
    
    Use .onlyShowPlayer to filter auras for spells cast by the player.
    ]]
    
    
    return function(self, unit, statusbar)
    
        --Get information about the aura(s) if present
        local result = cRefactored.queryAura(self, dot.aura)
        
        --If it was found and its ours or we don't care
        if result and (result.isMine or not dot.onlyShowPlayer) then
            
            --Set things up on the first call
            if not statusbar:IsShown() then 
                statusbar:Show()
                statusbar:SetMinMaxValues(0,result.duration)
        
                --Create a reactor call to update the bar state
                statusbar.reactorCall = function()
                    local result = cRefactored.queryAura(self, dot.aura)
                    if result then statusbar:SetValue(result.expirationTime-GetTime()) end
                end
                cRefactored.reactor:registerCall(statusbar.reactorCall)
                
            end
        
        else
            if statusbar:IsShown() then
                cRefactored.reactor:unregisterCall(statusbar.reactorCall)
                statusbar:Hide()
            end
        end
    
    end
    
end

function cRefactored.timerHandlerShowStacks(dot)
    --[[
    Like timerHandlerDuration this sets the length of the status bar based on 
    the remaining duration for the watched aura.  But we also show n-1 textures
    on the right hand side of the bar representing n stacks of the aura. Dots
    using this handler should define a texture with .showStackTexture and
    optionally a .dotColor to use when showing the dots.
    
    [Currently assumes stacks accumulate and all expire simultaneously -- I 
    can't think of an effect type which violates this assumption...]
    
    Hides the bar if the frame doesn't have the aura.
    
    Use .onlyShowPlayer to filter auras for spells cast by the player.
    ]]
    
    return function(self, unit, statusbar)
    
        --Get information about the aura(s) if present
        local result = cRefactored.queryAura(self, dot.aura)
        
        --If it was found and it's ours or we don't care
        if result and (result.isMine or not dot.onlyShowPlayer) then
            
            --Set things up on the first call
            if not statusbar:IsShown() then 
                statusbar:Show()
                statusbar:SetMinMaxValues(0,result.duration)
        
                --Create a reactor call to update the bar state
                statusbar.reactorCall = function()
                    local result = cRefactored.queryAura(self, dot.aura)
                    if result then statusbar:SetValue(result.expirationTime-GetTime()) end
                end
                cRefactored.reactor:registerCall(statusbar.reactorCall)
                
            end
            
            --Check the number of dots shown
            if result.count > 1 and dot.showStackTexture then
                
                if not statusbar.stackDots then statusbar.stackDots = {} end
                if #statusbar.stackDots < result.count then
                    for i=(#statusbar.stackDots+1),(result.count-1) do
                        local newDot = statusbar:CreateTexture(nil, 'OVERLAY')
                        newDot:SetTexture(dot.showStackTexture)
                        newDot:SetHeight(statusbar:GetHeight()+1)
                        newDot:SetWidth(statusbar:GetHeight()+1)
                        --newDot:SetPoint('TOPRIGHT', (#statusbar.stackDots>0 and statusbar.stackDots[#statusbar.stackDots-1] or statusbar), (#statusbar.stackDots>0 and 'TOPLEFT' or 'TOPRIGHT'))
                        if #statusbar.stackDots == 0 then
                            newDot:SetPoint('RIGHT', statusbar, 'RIGHT')
                        else
                            newDot:SetPoint('TOPRIGHT', statusbar.stackDots[#statusbar.stackDots], 'TOPLEFT')
                        end
                        newDot:SetVertexColor(unpack(dot.barColor))
                        table.insert(statusbar.stackDots, newDot)
                    end
                end
                for i,dotTex in ipairs(statusbar.stackDots) do
                    if i <= (result.count-1) then dotTex:Show()
                    else dotTex:Hide() end
                end
                
            end
        
        else
            if statusbar:IsShown() then
                cRefactored.reactor:unregisterCall(statusbar.reactorCall)
                if statusbar.stackDots then
                    for i,dotTex in ipairs(statusbar.stackDots) do dotTex:Hide() end
                end
                statusbar:Hide()
            end
        end
    
    end

end



function cRefactored.timerHandlerLifebloom(dot)
    
    
    return function(self, unit, statusbar)
    
        --Get information about the aura(s) if present
        local result = cRefactored.queryAura(self, dot.aura)
        
        --If it was found and its ours or we don't care
        if result and (result.isMine or not dot.onlyShowPlayer) then
            
            --Set things up on the first call
            if not statusbar:IsShown() then 
                statusbar:Show()
                statusbar:SetMinMaxValues(0,result.duration)
        
                --Create a reactor call to update the bar state
                statusbar.reactorCall = function()
                    local result = cRefactored.queryAura(self, dot.aura)
                    if result then statusbar:SetValue(result.expirationTime-GetTime()) end
                end
                cRefactored.reactor:registerCall(statusbar.reactorCall)
                
            end
        
            --Color bar based on stack count
            local barColor = {unpack(dot.barColor)}
            if result.count == 1 then
                barColor = {0.10, 0.50, 0.10, 1.0}
            elseif result.count == 2 then
                barColor = {0.23, 0.65, 0.23, 1.0}
            end
            statusbar:SetStatusBarColor(unpack(barColor))

        else
            if statusbar:IsShown() then
                cRefactored.reactor:unregisterCall(statusbar.reactorCall)
                statusbar:Hide()
            end
        end
    
    end
    
end

function cRefactored.adjustForDebuffCount(target, parent, setPoint)
    --[[
    Sleazy little function that will adjust a setPoint based on how many rows
    of debuffs are showing.  Only use if parent has a .Debuffs on it and
    setPoints are a full 5 member point table]]
    
    local debuffs = parent:GetParent().Debuffs
    
    if (not debuffs) or (not debuffs.visibleDebuffs) then return setPoint end
    
    local rows = (debuffs.visibleDebuffs==0 and 0 or math.floor(debuffs.visibleDebuffs/10) + (debuffs.visibleDebuffs % 10 ~= 0 and 1 or 0))
    local rowOffset = (((23*0.97)+1)*rows) + (rows==0 and 0 or 4)
    
    setPoint[5] = setPoint[5]-rowOffset
    
    return setPoint
    
end

--Utilities for decision making in config structures (return decorators)
function cRefactored.basedOnPlayerClass(classes)
    --[[
    classes should be a table with classname keys and decorators or tables
    containing decorators as values.
    
    classnames should be english and all uppercase (2nd value returned by 
    UnitClass).
    ]]--
    
    local playerClass = select(2,UnitClass("player"))
    local dec = classes[playerClass]
    
    local func
    if type(dec)=='table' then
        func = function(self,unit,config)
            for i,f in ipairs(dec) do f(self,unit,config) end
        end
    else func = dec
    end
    
    return func
    
end
